package com.gitee.wsl.jvm.task.thread.executor

import com.gitee.wsl.jvm.task.thread.Scheduler
import java.util.Collections
import java.util.Deque
import java.util.PriorityQueue
import java.util.concurrent.Executor
import java.util.concurrent.ExecutorService

class DequeLoopExecutor(val pool: ExecutorService = Scheduler.executor, private val idleCallback: Runnable? = null):
    Executor {
    private val readyAsyncCalls = PriorityQueue<Runnable>()

    private val runningAsyncCalls = ArrayDeque<Runnable>()

    private val runningSyncCalls = ArrayDeque<Runnable>()

    @get:Synchronized
    var maxRequests = 64
        set(maxRequests) {
            require(maxRequests >= 1) { "max < 1: $maxRequests" }
            synchronized(this) {
                field = maxRequests
            }
            promoteAndExecute()
        }

    override fun execute(call: Runnable) {
        synchronized(this) {
            readyAsyncCalls.add(call)
        }
        promoteAndExecute()
    }

    fun enqueue(call: Runnable) {
        synchronized(this) {
            readyAsyncCalls.add(call)
        }
        promoteAndExecute()
    }


    /*private fun promoteAndExecute() {
        val executableCalls = mutableListOf<Runnable>()
        synchronized(this) {
            val i = readyAsyncCalls.iterator()
            while (i.hasNext()) {
                val asyncCall = i.next()

                if (runningAsyncCalls.size >= this.maxRequests) break

                //if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue
                i.remove()
                //asyncCall.callsPerHost.incrementAndGet()
                executableCalls.add(asyncCall)

                runningAsyncCalls.add(asyncCall)
            }
        }
        for (i in 0 until executableCalls.size) {
            val asyncCall = executableCalls[i]
            pool.execute(asyncCall)
        }
    }*/

    private fun promoteAndExecute(): Boolean {
        val executableCalls = mutableListOf<Runnable>()
        val isRunning: Boolean
        synchronized(this) {
            while (!readyAsyncCalls.isEmpty()) {
                if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
                val asyncCall = readyAsyncCalls.poll()!!
                executableCalls.add(asyncCall)
                runningAsyncCalls.add(asyncCall)
            }
            isRunning = runningCallsCount() > 0
        }
        for (i in 0 until executableCalls.size) {
            val asyncCall = executableCalls[i]
            pool.execute(asyncCall)
        }

        return isRunning
    }


    @Synchronized
    fun queuedCalls(): List<Runnable> {
        return Collections.unmodifiableList(readyAsyncCalls.toList())
    }

    /** Returns a snapshot of the calls currently being executed. */
    @Synchronized
    fun runningCalls(): List<Runnable> {
        return Collections.unmodifiableList(runningSyncCalls + runningAsyncCalls.map { it })
    }

    @Synchronized
    fun queuedCallsCount(): Int = readyAsyncCalls.size

    @Synchronized
    fun runningCallsCount(): Int = runningAsyncCalls.size + runningSyncCalls.size

    /*@Synchronized
    internal fun cancelAll() {
        runningAsyncCalls.forEach { it.cancel() }
        runningSyncCalls.forEach { it.cancel() }
    }

    @Synchronized
    internal fun cancelAllSafely() {
        runningAsyncCalls.forEach { it.call.cancelSafely() }
        runningSyncCalls.forEach { it.cancelSafely() }
    }*/

    private fun <T> finished(calls: Deque<T>, call: T) {
        val idleCallback: Runnable?
        synchronized(this) {
            if (!calls.remove(call)) throw AssertionError("Call wasn't in-flight!")
            idleCallback = this.idleCallback
        }

        val isRunning = promoteAndExecute()
        if (!isRunning && idleCallback != null) {
            idleCallback.run()
        }
    }
}
